home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Entertainment / MacMud / Mud 4.0 / sprintf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-16  |  28.6 KB  |  907 lines  |  [TEXT/MPS ]

  1. /*
  2.  * sprintf.c v1.05+ for LPMud 3.0.52 (+ plus mappings)
  3.  *
  4.  * An implementation of (s)printf() for LPC, with quite a few
  5.  * extensions (note that as no floating point exists, some parameters
  6.  * have slightly different meaning or restrictions to "standard"
  7.  * (s)printf.)  Implemented by Lynscar (Sean A Reith).
  8.  *
  9.  * This version supports the following as modifiers:
  10.  *  " "   pad positive integers with a space.
  11.  *  "+"   pad positive integers with a plus sign.
  12.  *  "-"   left adjusted within field size.
  13.  *        NB: std (s)printf() defaults to right justification, which is
  14.  *            unnatural in the context of a mainly string based language
  15.  *            but has been retained for "compatability" ;)
  16.  *  "|"   centered within field size.
  17.  *  "="   column mode if strings are greater than field size.  this is only
  18.  *        meaningful with strings, all other types ignore
  19.  *        this.  columns are auto-magically word wrapped.
  20.  *  "#"   table mode, print a list of '\n' separated 'words' in a
  21.  *        table within the field size.  only meaningful with strings.
  22.  *   n    specifies the field size, a '*' specifies to use the corresponding
  23.  *        arg as the field size.  if n is prepended with a zero, then is padded
  24.  *        zeros, else it is padded with spaces (or specified pad string).
  25.  *  "."n  presision of n, simple strings truncate after this (if presision is
  26.  *        greater than field size, then field size = presision), tables use
  27.  *        presision to specify the number of columns (if presision not specified
  28.  *        then tables calculate a best fit), all other types ignore this.
  29.  *  ":"n  n specifies the fs _and_ the presision, if n is prepended by a zero
  30.  *        then it is padded with zeros instead of spaces.
  31.  *  "@"   the argument is an array.  the corresponding format_info (minus the
  32.  *        "@") is applyed to each element of the array.
  33.  *  "'X'" The char(s) between the single-quotes are used to pad to field
  34.  *        size (defaults to space) (if both a zero (in front of field
  35.  *        size) and a pad string are specified, the one specified second
  36.  *        overrules).  NOTE:  to include "'" in the pad string, you must
  37.  *        use "\\'" (as the backslash has to be escaped past the
  38.  *        interpreter), similarly, to include "\" requires "\\\\".
  39.  * The following are the possible type specifiers.
  40.  *  "%"   in which case no arguments are interpreted, and a "%" is inserted, and
  41.  *        all modifiers are ignored.
  42.  *  "O"   the argument is an LPC datatype.
  43.  *  "s"   the argument is a string.
  44.  *  "d"   the integer arg is printed in decimal.
  45.  *  "i"   as d.
  46.  *  "c"   the integer arg is to be printed as a character.
  47.  *  "o"   the integer arg is printed in octal.
  48.  *  "x"   the integer arg is printed in hex.
  49.  *  "X"   the integer arg is printed in hex (in capitals).
  50.  */
  51.  
  52. #include <stdio.h>
  53. #include <string.h>
  54. #include <setjmp.h>
  55. #include <sys/types.h>
  56.  
  57. #include "config.h"
  58. #include "lint.h"
  59. #include "lang.h"
  60. #include "stdio.h"
  61. #include "interpret.h"
  62. #include "object.h"
  63. #include "sent.h"
  64.  
  65. #ifdef mac
  66. #include "mac.h"
  67. #endif
  68.  
  69. /*
  70.  * If this #define is defined then error messages are returned,
  71.  * otherwise error() is called (ie: A "wrongness in the fabric...")
  72.  */
  73. #define RETURN_ERROR_MESSAGES
  74.  
  75. #if defined(F_SPRINTF) || defined(F_PRINTF)
  76.  
  77. extern char *xalloc(), *string_copy();
  78. extern void free_svalue PROT((struct svalue *));
  79.  
  80. extern struct object *current_object;
  81.  
  82. typedef unsigned int format_info;
  83. /*
  84.  * Format of format_info:
  85.  *   00000000 0000xxxx : argument type:
  86.  *                0000 : type not found yet;
  87.  *                0001 : error type not found;
  88.  *                0010 : percent sign, null argument;
  89.  *                0011 : LPC datatype;
  90.  *                0100 : string;
  91.  *                1000 : integer;
  92.  *                1001 : char;
  93.  *                1010 : octal;
  94.  *                1011 : hex;
  95.  *                1100 : HEX;
  96.  *   00000000 00xx0000 : justification:
  97.  *                00 : right;
  98.  *                01 : centre;
  99.  *                10 : left;
  100.  *   00000000 xx000000 : positive pad char:
  101.  *                00 : none;
  102.  *                01 : ' ';
  103.  *                10 : '+';
  104.  *   0000000x 00000000 : array mode?
  105.  *   000000x0 00000000 : column mode?
  106.  *   00000x00 00000000 : table mode?
  107.  */
  108.  
  109. #define INFO_T 0xF
  110. #define INFO_T_ERROR 0x1
  111. #define INFO_T_NULL 0x2
  112. #define INFO_T_LPC 0x3
  113. #define INFO_T_STRING 0x4
  114. #define INFO_T_INT 0x8
  115. #define INFO_T_CHAR 0x9
  116. #define INFO_T_OCT 0xA
  117. #define INFO_T_HEX 0xB
  118. #define INFO_T_C_HEX 0xC
  119.  
  120. #define INFO_J 0x30
  121. #define INFO_J_CENTRE 0x10
  122. #define INFO_J_LEFT 0x20
  123.  
  124. #define INFO_PP 0xC0
  125. #define INFO_PP_SPACE 0x40
  126. #define INFO_PP_PLUS 0x80
  127.  
  128. #define INFO_ARRAY 0x100
  129. #define INFO_COLS 0x200
  130. #define INFO_TABLE 0x400
  131.  
  132. #define BUFF_SIZE 10000
  133.  
  134. #define ERROR(x) longjmp(error_jmp, x)
  135. #define ERR_BUFF_OVERFLOW    0x1    /* buffer overflowed */
  136. #define ERR_TO_FEW_ARGS        0x2    /* more arguments spec'ed than passed */
  137. #define ERR_INVALID_STAR    0x3    /* invalid arg to * */
  138. #define ERR_PRES_EXPECTED    0x4    /* expected presision not found */
  139. #define ERR_INVALID_FORMAT_STR    0x5    /* error in format string */
  140. #define ERR_INCORRECT_ARG_S    0x6    /* invalid arg to %s */
  141. #define ERR_CST_REQUIRES_FS    0x7    /* field size not given for c/t */
  142. #define ERR_BAD_INT_TYPE    0x8    /* bad integer type... */
  143. #define ERR_UNDEFINED_TYPE    0x9    /* undefined type found */
  144. #define ERR_QUOTE_EXPECTED    0xA    /* expected ' not found */
  145. #define ERR_UNEXPECTED_EOS    0xB    /* fs terminated unexpectedly */
  146. #define ERR_NULL_PS        0xC    /* pad string is null */
  147.  
  148. #define ADD_CHAR(x) {\
  149.   buff[bpos++] = x;\
  150.   if (bpos>BUFF_SIZE) ERROR(ERR_BUFF_OVERFLOW); \
  151.   curpos++;\
  152. }
  153.  
  154. #define GET_NEXT_ARG {\
  155.   if (++arg >= argc) ERROR(ERR_TO_FEW_ARGS); \
  156.   carg = (argv+arg);\
  157. }
  158.  
  159. #define SAVE_CHAR(pointer) {\
  160.   savechars *new;\
  161.   new = (savechars *)xalloc(sizeof(savechars));\
  162.   new->what = *(pointer);\
  163.   new->where = pointer;\
  164.   new->next = saves;\
  165.   saves = new;\
  166. }
  167.  
  168. /*
  169.  * list of characters to restore before exiting.
  170.  */
  171. typedef struct SaveChars {
  172.   char what;
  173.   char *where;
  174.   struct SaveChars *next;
  175. } savechars;
  176.  
  177. typedef struct ColumnSlashTable {
  178.   union CSTData {
  179.     char *col;            /* column data */
  180.     char **tab;            /* table data */
  181.   } d;                /* d == data */
  182.   unsigned short int nocols;    /* number of columns in table *sigh* */
  183.   char *pad;
  184.   unsigned int start;        /* starting cursor position */
  185.   unsigned int size;        /* column/table width */
  186.   int pres;            /* presision */
  187.   format_info info;        /* formatting data */
  188.   struct ColumnSlashTable *next;
  189. } cst;                /* Columns Slash Tables */
  190.  
  191. static char buff[BUFF_SIZE];    /* buffer for returned string */
  192. unsigned int bpos;        /* position in buff */
  193. unsigned int curpos;        /* cursor position */
  194. jmp_buf error_jmp;        /* for error longjmp()s */
  195.  
  196. /*
  197.  * Probably should make this a #define...
  198.  */
  199. void stradd(dst, size, add)
  200.   char **dst, *add;
  201.   int *size;
  202. {
  203.   int i;
  204.  
  205.   if ((i = (strlen(*dst) + strlen(add))) >= *size) {
  206.     *size += i + 1;
  207.     *dst = xrealloc(*dst, *size);
  208.   }
  209.   strcat(*dst, add);
  210. } /* end of stradd() */
  211.  
  212. void numadd(dst, size, num)
  213.   char **dst;
  214.   int *size, num;
  215. {
  216.   int i,j,nve;
  217.  
  218.   if (num < 0) { num *= -1; nve=1; } else nve=0;
  219.   for (i=10, j=nve+1; num >= i; i*=10, j++) ;
  220.   i = strlen(*dst);
  221.   if ((i + j) >= *size) {
  222.     *size += i + j + 1;
  223.     *dst = xrealloc(*dst, *size);
  224.   }
  225.   (*dst)[i+j] = '\0';
  226.   if (nve) (*dst)[i-1] = '-'; else i--;
  227.   for (; j; j--, num /= 10) (*dst)[i+j] = (num%10) + '0';
  228. } /* end of num_add() */
  229.  
  230. /*
  231.  * This is a function purely because stradd() is, to keep same param
  232.  * passing...
  233.  */
  234. void add_indent(dst, size, indent)
  235.   char **dst;
  236.   int *size, indent;
  237. {
  238.   int i;
  239.  
  240.   i = strlen(*dst);
  241.   if ((i + indent) >= *size) {
  242.     *size += i + indent + 1;
  243.     *dst = xrealloc(*dst, *size);
  244.   }
  245.   for (;indent;indent--) (*dst)[i++] = ' ';
  246.   (*dst)[i] = '\0';
  247. }
  248.  
  249. /*
  250.  * Converts any LPC datatype into an arbitrary string format
  251.  * and returns a pointer to this string.
  252.  * Scary number of parameters for a recursive function.
  253.  */
  254. void svalue_to_string(obj, str, size, indent, trailing)
  255.   struct svalue *obj;
  256.   char **str;
  257.   int size, indent, trailing;
  258. {
  259.   int i;
  260.  
  261.   add_indent(str, &size, indent);
  262.   switch (obj->type) {
  263.     case T_INVALID:
  264.       stradd(str,&size,"T_INVALID");
  265.       break;
  266.     case T_LVALUE:
  267.       stradd(str, &size, "lvalue: ");
  268.       svalue_to_string(obj->u.lvalue, str, size, indent+2, trailing);
  269.       break;
  270.     case T_NUMBER:
  271.       numadd(str, &size, obj->u.number);
  272.       break;
  273.     case T_STRING:
  274.       stradd(str, &size, "\"");
  275.       stradd(str, &size, obj->u.string);
  276.       stradd(str, &size, "\"");
  277.       break;
  278.     case T_POINTER:
  279.       if (!(obj->u.vec->size)) {
  280.         stradd(str, &size, "({ })");
  281.       } else {
  282.         stradd(str, &size, "({ /* sizeof() == ");
  283.         numadd(str, &size, obj->u.vec->size);
  284.         stradd(str, &size, " */\n");
  285.         for (i=0; i<(obj->u.vec->size)-1; i++)
  286.           svalue_to_string(&(obj->u.vec->item[i]), str, size, indent+2, 1);
  287.         svalue_to_string(&(obj->u.vec->item[i]), str, size, indent+2, 0);
  288.         stradd(str, &size, "\n");
  289.         add_indent(str, &size, indent);
  290.         stradd(str, &size, "})");
  291.       }
  292.       break;
  293.     case T_OBJECT:
  294.     {
  295.       struct svalue *temp;
  296.  
  297.       stradd(str, &size, obj->u.ob->name);
  298.       push_object(obj->u.ob);
  299.       temp = apply_master_ob("object_name", 1);
  300.       if (temp && (temp->type == T_STRING)) {
  301.         stradd(str, &size, " (\"");
  302.         stradd(str, &size, temp->u.string);
  303.         stradd(str, &size, "\")");
  304.       }
  305.       /*
  306.        * These flags aren't that useful...
  307.        *
  308.       if (obj->u.ob->flags & O_HEART_BEAT) stradd(str,&size," (hb)");
  309.       if (obj->u.ob->flags & O_IS_WIZARD) stradd(str,&size," (wiz)");
  310.       if (obj->u.ob->flags & O_ENABLE_COMMANDS) stradd(str,&size," (enabled)");
  311.       if (obj->u.ob->flags & O_CLONE) stradd(str,&size," (clone)");
  312.       if (obj->u.ob->flags & O_DESTRUCTED) stradd(str,&size," (destructed)");
  313.       if (obj->u.ob->flags & O_SWAPPED) stradd(str,&size," (swapped)");
  314.       if (obj->u.ob->flags & O_ONCE_INTERACTIVE) stradd(str,&size," (x-activ)");
  315.       if (obj->u.ob->flags & O_APPROVED) stradd(str,&size," (ok)");
  316.       if (obj->u.ob->flags & O_RESET_STATE) stradd(str,&size," (reset)");
  317.       if (obj->u.ob->flags & O_WILL_CLEAN_UP) stradd(str,&size," (clean up)");
  318.        */
  319.       break;
  320.     }
  321.     case T_MAPPING:
  322.       if (!(obj->u.vec->item[0].u.vec->size)) {
  323.         stradd(str, &size, "([ ])");
  324.       } else {
  325.     struct vector *p, *q;
  326.  
  327.     p = obj->u.vec->item[0].u.vec;
  328.     q = obj->u.vec->item[1].u.vec;
  329.         stradd(str, &size, "([ /* sizeof() == ");
  330.         numadd(str, &size, p->size);
  331.         stradd(str, &size, " */\n");
  332.         for (i=0; i < p->size; i++) {
  333.             svalue_to_string(&(p->item[i]), str, size, indent+2, 0);
  334.             stradd(str, &size, " : ");
  335.             svalue_to_string(&(q->item[i]), str, size, 0, 1);
  336.         }
  337.         add_indent(str, &size, indent);
  338.         stradd(str, &size, "])");
  339.       }
  340.       break;
  341.  
  342.     default:
  343.       stradd(str, &size, "!ERROR: GARBAGE SVALUE!");
  344.   } /* end of switch (obj->type) */
  345.   if (trailing) stradd(str, &size, ",\n");
  346. } /* end of svalue_to_string() */
  347.  
  348. /*
  349.  * Adds the string "str" to the buff after justifying it within "fs".
  350.  * "trailing" is a flag which is set if trailing justification is to be done.
  351.  * "str" is unmodified.  trailing is, of course, ignored in the case
  352.  * of right justification.
  353.  */
  354. void add_justified(str, pad, fs, finfo, trailing)
  355.   char *str, *pad;
  356.   int fs;
  357.   format_info finfo;
  358.   short int trailing;
  359. {
  360.   int i, len;
  361.  
  362.   len = strlen(str);
  363.   switch(finfo & INFO_J) {
  364.     case INFO_J_LEFT:
  365.       for (i=0; i<len; i++) ADD_CHAR(str[i]);
  366.       fs -= len;
  367.       len = strlen(pad);
  368.       if (trailing) for (i=0; fs>0; i++, fs--) {
  369.         if (pad[i%len] == '\\') i++;
  370.         ADD_CHAR(pad[i%len]);
  371.       }
  372.       break;
  373.     case INFO_J_CENTRE: {
  374.       int j, l;
  375.  
  376.       l = strlen(pad);
  377.       j = (fs - len)/2 + (fs - len)%2;
  378.       for (i=0; i<j; i++) {
  379.         if (pad[i%len] == '\\') { i++; j++; }
  380.         ADD_CHAR(pad[i%l]);
  381.       }
  382.       for (i=0; i<len; i++) ADD_CHAR(str[i]);
  383.       j = (fs - len)/2;
  384.       if (trailing) for (i=0; i<j; i++) {
  385.         if (pad[i%l] == '\\') { i++; j++; }
  386.         ADD_CHAR(pad[i%l]);
  387.       }
  388.       break;
  389.     }
  390.     default: { /* std (s)printf defaults to right justification */
  391.       int l;
  392.  
  393.       fs -= len;
  394.       l = strlen(pad);
  395.       for (i=0; i<fs; i++) {
  396.         if (pad[i%l] == '\\') { i++; fs++; }
  397.         ADD_CHAR(pad[i%l]);
  398.       }
  399.       for (i=0; i<len; i++) ADD_CHAR(str[i]);
  400.     }
  401.   }
  402. } /* end of add_justified() */
  403.  
  404. /*
  405.  * Adds "column" to the buffer.
  406.  * Returns 0 is column not finished.
  407.  * Returns 1 if column completed.
  408.  * Returns 2 if column completed has a \n at the end.
  409.  */
  410. int add_column(column, trailing)
  411.   cst **column;
  412.   short int trailing;
  413. {
  414.   register unsigned int done;
  415.   unsigned int save;
  416. #define COL (*column)
  417. #define COL_D (COL->d.col)
  418.  
  419.   for (done=0;(done<COL->pres) && COL_D[done] && (COL_D[done]!='\n');done++);
  420.   if (COL_D[done] && (COL_D[done]!='\n')) {
  421.     save = done;
  422.     for (; done && (COL_D[done]!=' '); done--);
  423.     /*
  424.      * handle larger than column size words...
  425.      */
  426.     if (!done) done = save;
  427.   }
  428.   save = COL_D[done];
  429.   COL_D[done] = '\0';
  430.   add_justified(COL_D, COL->pad, COL->size, COL->info,
  431.                 (trailing || (COL->next)));
  432.   COL_D[done] = save;
  433.   COL_D += done; /* inc'ed below ... */
  434.   /*
  435.    * if this or the next character is a NULL then take this column out
  436.    * of the list.
  437.    */
  438.   if (!(*COL_D) || !(*(++COL_D))) {
  439.     cst *temp;
  440.     int ret;
  441.  
  442.     if (*(COL_D-1) == '\n') ret = 2; else ret = 1;
  443.     temp = COL->next;
  444.     xfree((char *)COL);
  445.     COL = temp;
  446.     return ret;
  447.   }
  448.   return 0;
  449. } /* end of add_column() */
  450.  
  451. /*
  452.  * Adds "table" to the buffer.
  453.  * Returns 0 if table not completed.
  454.  * Returns 1 if table completed.
  455.  */
  456. int add_table(table, trailing)
  457.   cst **table;
  458.   short int trailing;
  459. {
  460.   char save;
  461.   register unsigned int done, i;
  462. #define TAB (*table)
  463. #define TAB_D (TAB->d.tab[i])
  464.  
  465.   for (i=0; i < TAB->nocols && TAB_D; i++) {
  466.     for (done=0;(TAB_D[done])&&(TAB_D[done] != '\n');done++);
  467.     save = TAB_D[done];
  468.     TAB_D[done] = '\0';
  469.     add_justified(TAB_D, TAB->pad, TAB->size, TAB->info, 
  470.                   (trailing || (i < TAB->nocols-1) || (TAB->next)));
  471.     TAB_D[done] = save;
  472.     TAB_D += done; /* inc'ed next line ... */
  473.     if (!(*TAB_D) || !(*(++TAB_D))) TAB_D = 0;
  474.   }
  475.   if (trailing && i < TAB->nocols)
  476.     for (; i < TAB->nocols; i++)
  477.       for (done = 0; done < TAB->size; done++) ADD_CHAR(' ');
  478.   if (!TAB->d.tab[0]) {
  479.     cst *temp;
  480.  
  481.     temp = TAB->next;
  482.     xfree((char *)TAB);
  483.     TAB = temp;
  484.     return 1;
  485.   }
  486.   return 0;
  487. } /* end of add_table() */
  488.  
  489. /*
  490.  * THE (s)printf() function.
  491.  * It returns a pointer to it's internal buffer (or a string in the text
  492.  * segment) thus, the string must be copied if it has to survive after
  493.  * this function is called again, or if it's going to be modified (esp.
  494.  * if it risks being free()ed).
  495.  */
  496. char *string_print_formatted(format_str, argc, argv)
  497.   char *format_str;
  498.   int argc;
  499.   struct svalue *argv;
  500. {
  501.   format_info finfo;
  502.   savechars *saves;    /* chars to restore */
  503.   cst *csts;        /* list of columns/tables to be done */
  504.   struct svalue *carg;    /* current arg */
  505.   unsigned int nelemno;    /* next offset into array */
  506.   unsigned int fpos;    /* position in format_str */
  507.   unsigned int arg;    /* current arg number */
  508.   unsigned int fs;    /* field size */
  509.   int pres;        /* presision */
  510.   unsigned int i;
  511.   char *pad;        /* fs pad string */
  512.  
  513.   if ((i = setjmp(error_jmp))) { /* error handling */
  514.     char *err;
  515.  
  516.     switch(i) {
  517.       case ERR_BUFF_OVERFLOW:
  518.         err = "BUFF_SIZE overflowed...";
  519.         break;
  520.       case ERR_TO_FEW_ARGS:
  521.         err = "More arguments specified than passed.";
  522.         break;
  523.       case ERR_INVALID_STAR:
  524.         err = "Incorrect argument type to *.";
  525.         break;
  526.       case ERR_PRES_EXPECTED:
  527.         err = "Expected presision not found.";
  528.         break;
  529.       case ERR_INVALID_FORMAT_STR:
  530.         err = "Error in format string.";
  531.         break;
  532.       case ERR_INCORRECT_ARG_S:
  533.         err = "Incorrect argument to type %s";
  534.         break;
  535.       case ERR_CST_REQUIRES_FS:
  536.         err = "Column/table mode requires a field size.";
  537.         break;
  538.       case ERR_BAD_INT_TYPE:
  539.         err = "!feature - bad integer type!";
  540.         break;
  541.       case ERR_UNDEFINED_TYPE:
  542.         err = "!feature - undefined type!";
  543.         break;
  544.       case ERR_QUOTE_EXPECTED:
  545.         err = "Quote expected in format string.";
  546.         break;
  547.       case ERR_UNEXPECTED_EOS:
  548.         err = "Unexpected end of format string.";
  549.         break;
  550.       case ERR_NULL_PS:
  551.         err = "Null pad string specified.";
  552.         break;
  553.       default:
  554. #ifdef RETURN_ERROR_MESSAGES
  555.         sprintf(buff,
  556.           "ERROR: (s)printf(): !feature - undefined error 0x%X !\n", i);
  557.         fprintf(stderr, "%s:%d: %s", current_object->name,
  558.                                      get_line_number_if_any(), buff);
  559.         return buff;
  560. #else
  561.         error("ERROR: (s)printf(): !feature - undefined error 0x%X !\n", i);
  562. #endif /* RETURN_ERROR_MESSAGES */
  563.     }
  564. #ifdef RETURN_ERROR_MESSAGES
  565.     sprintf(buff, "ERROR: (s)printf(): %s\n", err);
  566.     fprintf(stderr, "%s:%d: %s", current_object->name,
  567.                                  get_line_number_if_any(), buff);
  568.     return buff;
  569. #else
  570.     error("ERROR: (s)printf(): %s\n", err);
  571. #endif /* RETURN_ERROR_MESSAGES */
  572.   }
  573.   arg = -1;
  574.   bpos = 0;
  575.   curpos = 0;
  576.   csts = 0;
  577.   saves = 0;
  578.   for (fpos=0; 1; fpos++) {
  579.     if ((format_str[fpos] == '\n') || (!format_str[fpos])) {
  580.       int column_stat = 0;
  581.  
  582.       if (!csts) {
  583.         if (!format_str[fpos]) break;
  584.         ADD_CHAR('\n');
  585.         curpos = 0;
  586.         continue;
  587.       }
  588.       ADD_CHAR('\n');
  589.       curpos = 0;
  590.       while (csts) {
  591.         cst **temp;
  592.  
  593.         temp = &csts;
  594.         while (*temp) {
  595.           if ((*temp)->info & INFO_COLS) {
  596.             if (*((*temp)->d.col-1) != '\n')
  597.               while (*((*temp)->d.col) == ' ') (*temp)->d.col++;
  598.             for (i=curpos; i<(*temp)->start; i++) ADD_CHAR(' ');
  599.             column_stat = add_column(temp, 0);
  600.             if (!column_stat) temp = &((*temp)->next);
  601.           } else {
  602.             for (i=curpos; i<(*temp)->start; i++) ADD_CHAR(' ');
  603.             if (!add_table(temp, 0)) temp = &((*temp)->next);
  604.           }
  605.         } /* of while (*temp) */
  606.         if (csts || format_str[fpos] == '\n')
  607.           ADD_CHAR('\n');
  608.         curpos = 0;
  609.       } /* of while (csts) */
  610.       if (column_stat == 2) ADD_CHAR('\n');
  611.       if (!format_str[fpos]) break;
  612.       continue;
  613.     }
  614.     if (format_str[fpos] == '%') {
  615.       if (format_str[fpos+1] == '%') {
  616.         ADD_CHAR('%');
  617.         fpos++;
  618.         continue;
  619.       }
  620.       GET_NEXT_ARG;
  621.       fs = 0;
  622.       pres = 0;
  623.       pad = " ";
  624.       finfo = 0;
  625.       for (fpos++; !(finfo & INFO_T); fpos++) {
  626.         if (!format_str[fpos]) {
  627.           finfo |= INFO_T_ERROR;
  628.           break;
  629.         }
  630.         if (((format_str[fpos] >= '0') && (format_str[fpos] <= '9'))
  631.             || (format_str[fpos] == '*')) {
  632.           if (pres == -1) { /* then looking for pres */
  633.             if (format_str[fpos] == '*') {
  634.               if (carg->type != T_NUMBER)
  635.                 ERROR(ERR_INVALID_STAR);
  636.               pres = carg->u.number;
  637.               GET_NEXT_ARG;
  638.               continue;
  639.             }
  640.             pres = format_str[fpos] - '0';
  641.             for (fpos++;
  642.                  (format_str[fpos]>='0')&&(format_str[fpos]<='9'); fpos++) {
  643.               pres = pres*10 + format_str[fpos] - '0';
  644.             }
  645.           } else { /* then is fs (and maybe pres) */
  646.             if ((format_str[fpos] == '0') && (((format_str[fpos+1] >= '1')
  647.                 && (format_str[fpos+1] <= '9')) || (format_str[fpos+1] == '*')))
  648.               pad = "0";
  649.             else {
  650.               if (format_str[fpos] == '*') {
  651.                 if (carg->type != T_NUMBER)
  652.                   ERROR(ERR_INVALID_STAR);
  653.                 fs = carg->u.number;
  654.                 if (pres == -2) pres = fs; /* colon */
  655.                 GET_NEXT_ARG;
  656.                 continue;
  657.               }
  658.               fs = format_str[fpos] - '0';
  659.             }
  660.             for (fpos++;
  661.                  (format_str[fpos]>='0')&&(format_str[fpos]<='9'); fpos++) {
  662.               fs = fs*10 + format_str[fpos] - '0';
  663.             }
  664.             if (pres == -2) { /* colon */
  665.               pres = fs;
  666.             }
  667.           }
  668.           fpos--; /* bout to get incremented */
  669.           continue;
  670.         }
  671.         switch (format_str[fpos]) {
  672.           case ' ': finfo |= INFO_PP_SPACE; break;
  673.           case '+': finfo |= INFO_PP_PLUS; break;
  674.           case '-': finfo |= INFO_J_LEFT; break;
  675.           case '|': finfo |= INFO_J_CENTRE; break;
  676.           case '@': finfo |= INFO_ARRAY; break;
  677.           case '=': finfo |= INFO_COLS; break;
  678.           case '#': finfo |= INFO_TABLE; break;
  679.           case '.': pres = -1; break;
  680.           case ':': pres = -2; break;
  681.           case '%': finfo |= INFO_T_NULL; break; /* never reached */
  682.           case 'O': finfo |= INFO_T_LPC; break;
  683.           case 's': finfo |= INFO_T_STRING; break;
  684.           case 'd': finfo |= INFO_T_INT; break;
  685.           case 'i': finfo |= INFO_T_INT; break;
  686.           case 'c': finfo |= INFO_T_CHAR; break;
  687.           case 'o': finfo |= INFO_T_OCT; break;
  688.           case 'x': finfo |= INFO_T_HEX; break;
  689.           case 'X': finfo |= INFO_T_C_HEX; break;
  690.           case '\'':
  691.             pad = &(format_str[++fpos]);
  692.             while (1) {
  693.               if (!format_str[fpos]) ERROR(ERR_UNEXPECTED_EOS);
  694.               if (format_str[fpos] == '\\') { fpos += 2; continue; }
  695.               if (format_str[fpos] == '\'') {
  696.                 if (format_str+fpos == pad) ERROR(ERR_NULL_PS);
  697.                 SAVE_CHAR(format_str+fpos);
  698.                 format_str[fpos] = '\0';
  699.                 break;
  700.               }
  701.               fpos++;
  702.             }
  703.             break;
  704.           default: finfo |= INFO_T_ERROR;
  705.         }
  706.       } /* end of for () */
  707.       if (pres < 0)
  708.         ERROR(ERR_PRES_EXPECTED);
  709.       /*
  710.        * now handle the different arg types...
  711.        */
  712.       if (finfo & INFO_ARRAY) {
  713.         if (carg->u.vec->size == 0) {
  714.           fpos--; /* 'bout to get incremented */
  715.           continue;
  716.         }
  717.         carg = (argv+arg)->u.vec->item;
  718.         nelemno = 1; /* next element number */
  719.       }
  720.       while (1) {
  721.         struct svalue *clean = 0;
  722.  
  723.         if ((finfo & INFO_T) == INFO_T_LPC) {
  724.           clean = (struct svalue *)xalloc(sizeof(struct svalue));
  725.           clean->type = T_STRING;
  726.           clean->string_type = STRING_MALLOC;
  727.           clean->u.string = (char *)xalloc(500);
  728.           clean->u.string[0] = '\0';
  729.           svalue_to_string(carg, &(clean->u.string), 500, 0, 0);
  730.           carg = clean;
  731.           finfo ^= INFO_T_LPC;
  732.           finfo |= INFO_T_STRING;
  733.         }
  734.         if ((finfo & INFO_T) == INFO_T_ERROR) {
  735.           ERROR(ERR_INVALID_FORMAT_STR);
  736.         } else if ((finfo & INFO_T) == INFO_T_NULL) {
  737.           /* never reached... */
  738.           fprintf(stderr, "%s: (s)printf: INFO_T_NULL.... found.\n",
  739.                   current_object->name);
  740.           ADD_CHAR('%');
  741.         } else if ((finfo & INFO_T) == INFO_T_STRING) {
  742.           int slen;
  743.  
  744.           if (carg->type != T_STRING)
  745.             ERROR(ERR_INCORRECT_ARG_S);
  746.           slen = strlen(carg->u.string);
  747.           if ((finfo & INFO_COLS) || (finfo & INFO_TABLE)) {
  748.             cst **temp;
  749.  
  750.             if (!fs)
  751.               ERROR(ERR_CST_REQUIRES_FS);
  752.  
  753.             temp = &csts;
  754.             while (*temp) temp = &((*temp)->next);
  755.             if (finfo & INFO_COLS) {
  756.               *temp = (cst *)xalloc(sizeof(cst));
  757.               (*temp)->next = 0;
  758.               (*temp)->d.col = carg->u.string;
  759.               (*temp)->pad = pad;
  760.               (*temp)->size = fs;
  761.               (*temp)->pres = (pres) ? pres : fs;
  762.               (*temp)->info = finfo;
  763.               (*temp)->start = curpos;
  764.               if ((add_column(temp, (((format_str[fpos] != '\n')
  765.                     && (format_str[fpos] != '\0')) || ((finfo & INFO_ARRAY)
  766.                       && (nelemno < (argv+arg)->u.vec->size)))) == 2)
  767.                   && !format_str[fpos]) {
  768.                 ADD_CHAR('\n');
  769.               }
  770.             } else { /* (finfo & INFO_TABLE) */
  771.               unsigned int n, len, max;
  772.  
  773. #define TABLE carg->u.string
  774.               (*temp) = (cst *)xalloc(sizeof(cst));
  775.               (*temp)->pad = pad;
  776.               (*temp)->info = finfo;
  777.               (*temp)->start = curpos;
  778.               (*temp)->next = 0;
  779.               max = len = 0;
  780.               n = 1;
  781.               for (i=0; TABLE[i]; i++) {
  782.                 if (TABLE[i] == '\n') {
  783.                   if (len > max) max = len;
  784.                   len = 0;
  785.                   if (TABLE[i+1]) n++;
  786.                   continue;
  787.                 }
  788.                 len++;
  789.               }
  790.               if (pres) {
  791.                 (*temp)->size = fs/pres;
  792.               } else {
  793.                 if (len > max) max = len; /* the null terminated word */
  794.                 pres = fs/(max+2); /* at least two separating spaces */
  795.                 if (!pres) pres = 1;
  796.                 (*temp)->size = fs/pres;
  797.               }
  798.               len = n/pres; /* length of average column */
  799.               if (n < pres) pres = n;
  800.               if (len*pres < n) len++;
  801.               if (len > 1 && n%pres) pres -= (pres - n%pres)/len;
  802.               (*temp)->d.tab = (char **)xalloc(pres*sizeof(char *));
  803.               (*temp)->nocols = pres; /* heavy sigh */
  804.               (*temp)->d.tab[0] = TABLE;
  805.               if (pres == 1) goto add_table_now;
  806.               i = 1; /* the next column number */
  807.               n = 0; /* the current "word" number in this column */
  808.               for (fs = 0; TABLE[fs]; fs++) { /* throwing away fs... */
  809.                 if (TABLE[fs] == '\n') {
  810.                   if (++n >= len) {
  811.                     SAVE_CHAR(((TABLE)+fs));
  812.                     TABLE[fs] = '\0';
  813.                     (*temp)->d.tab[i++] = TABLE+fs+1;
  814.                     if (i >= pres) goto add_table_now;
  815.                     n = 0;
  816.                   }
  817.                 }
  818.               }
  819. add_table_now:
  820.               add_table(temp, (((format_str[fpos] != '\n')
  821.                 && (format_str[fpos] != '\0')) || ((finfo & INFO_ARRAY)
  822.                   && (nelemno < (argv+arg)->u.vec->size))));
  823.             }
  824.           } else { /* not column or table */
  825.             if (pres && pres<slen) {
  826.               SAVE_CHAR(((carg->u.string)+pres));
  827.               carg->u.string[pres] = '\0';
  828.               slen = pres;
  829.             }
  830.             if (fs && fs>slen) {
  831.               add_justified(carg->u.string, pad, fs, finfo,
  832.           (((format_str[fpos] != '\n') && (format_str[fpos] != '\0'))
  833.           || ((finfo & INFO_ARRAY) && (nelemno < (argv+arg)->u.vec->size)))
  834.           || carg->u.string[slen-1] != '\n');
  835.             } else {
  836.               for (i=0; i<slen; i++) ADD_CHAR(carg->u.string[i]);
  837.             }
  838.           }
  839.         } else if (finfo & INFO_T_INT) { /* one of the integer types */
  840.           char cheat[4];
  841.           char temp[100];
  842.   
  843.           *cheat = '%';
  844.           i = 1;
  845.           switch (finfo & INFO_PP) {
  846.             case INFO_PP_SPACE: cheat[i++] = ' '; break;
  847.             case INFO_PP_PLUS: cheat[i++] = '+'; break;
  848.           }
  849.           switch (finfo & INFO_T) {
  850.             case INFO_T_INT: cheat[i++] = 'd'; break;
  851.             case INFO_T_CHAR: cheat[i++] = 'c'; break;
  852.             case INFO_T_OCT: cheat[i++] = 'o'; break;
  853.             case INFO_T_HEX: cheat[i++] = 'x'; break;
  854.             case INFO_T_C_HEX: cheat[i++] = 'X'; break;
  855.             default: ERROR(ERR_BAD_INT_TYPE);
  856.           }
  857.           if (carg->type != T_NUMBER) { /* sigh... */
  858. #ifdef RETURN_ERROR_MESSAGES
  859.             sprintf(buff,
  860.               "ERROR: (s)printf(): incorrect argument type to %%%c.\n",
  861.               cheat[i-1]);
  862.             fprintf(stderr, "%s:%d: %s", current_object->name,
  863.                                          get_line_number_if_any(), buff);
  864.             return buff;
  865. #else
  866.             error("ERROR: (s)printf(): incorrect argument type to %%%c.\n",
  867.                   cheat[i-1]);
  868. #endif /* RETURN_ERROR_MESSAGES */
  869.           }
  870.           cheat[i] = '\0';
  871.           sprintf(temp, cheat, carg->u.number);
  872.           {
  873.             int tmpl = strlen(temp);
  874.             if (pres && tmpl > pres) temp[pres] = '\0'; /* well.... */
  875.             if (tmpl < fs)
  876.               add_justified(temp, pad, fs, finfo,
  877.           (((format_str[fpos] != '\n') && (format_str[fpos] != '\0'))
  878.           || ((finfo & INFO_ARRAY) && (nelemno < (argv+arg)->u.vec->size))));
  879.             else
  880.               for (i=0; i<tmpl; i++) ADD_CHAR(temp[i]);
  881.           }
  882.         } else /* type not found */
  883.           ERROR(ERR_UNDEFINED_TYPE);
  884.         if (clean) free_svalue(clean);
  885.         if (!(finfo & INFO_ARRAY)) break;
  886.         if (nelemno >= (argv+arg)->u.vec->size) break;
  887.         carg = (argv+arg)->u.vec->item+nelemno++;
  888.       } /* end of while (1) */
  889.       fpos--; /* bout to get incremented */
  890.       continue;
  891.     }
  892.     ADD_CHAR(format_str[fpos]);
  893.   } /* end of for (fpos=0; 1; fpos++) */
  894.   ADD_CHAR('\0');
  895.   while (saves) {
  896.     savechars *tmp;
  897.     *(saves->where) = saves->what;
  898.     tmp = saves;
  899.     saves = saves->next;
  900.     xfree((char *)tmp);
  901.   }
  902.   return buff;
  903. } /* end of string_print_formatted() */
  904.  
  905. #endif /* defined(F_SPRINTF) || defined(F_PRINTF) */
  906.  
  907.